package org.smartboot.flow.core.component;


import org.smartboot.flow.core.Condition;
import org.smartboot.flow.core.EngineContext;
import org.smartboot.flow.core.Key;
import org.smartboot.flow.core.common.ComponentType;
import org.smartboot.flow.core.invoker.WrappedInvoker;
import org.smartboot.flow.core.util.AssertUtil;
import org.smartboot.flow.core.visitor.ComponentVisitor;
import org.smartboot.flow.core.visitor.ConditionVisitor;

/**
 * @author qinluo
 * @date 2022-11-12 21:54:48
 * @since 1.0.0
 */
public class IfComponent<T, S> extends Component<T, S>{

    private Condition<T, S> condition;
    private Component<T, S> thenComponent;
    private Component<T, S> elseComponent;

    public void setCondition(Condition<T, S> condition) {
        this.condition = condition;
    }

    public void setElseComponent(Component<T, S> elseComponent) {
        this.elseComponent = elseComponent;
    }

    public void setThenComponent(Component<T, S> thenComponent) {
        this.thenComponent = thenComponent;
    }

    @Override
    public int invoke(EngineContext<T, S> context) throws Throwable {
        Object test = condition.test(context);
        Component<T, S> execute = null;

        if (test != null && Boolean.parseBoolean(String.valueOf(test))) {
            execute = this.thenComponent;
        } else if (elseComponent != null) {
            execute = this.elseComponent;
        }


        if (execute != null) {
            context.putExt(Key.of(this), execute);
            WrappedInvoker.invoke(context, execute);
        }

        return 1;
    }

    @Override
    public boolean isRollbackable(EngineContext<T, S> context) {
        Component<T, S> executed = context.getExt(Key.of(this));
        return executed != null && executed.isRollbackable(context);
    }

    @Override
    public void rollback(EngineContext<T, S> context) {
        Component<T, S> executed = context.remove(Key.of(this));
        if (executed == null || !executed.isRollbackable(context)) {
            return;
        }

        WrappedInvoker.rollback(context, executed);
    }

    @Override
    public String describe() {
        return "if@" + condition.describe();
    }

    @Override
    public void accept(ComponentVisitor visitor) {
        visitor.visitAttributes(attributes);
        visitor.visitExtensionAttributes(this.get());
        ConditionVisitor conditionVisitor = visitor.visitCondition(condition.describe());
        if (conditionVisitor != null) {
            condition.visit(conditionVisitor);
        }

        visitor.visitSource(this);
        ComponentVisitor thenVisitor = visitor.visitComponent(thenComponent.getType(), thenComponent.getName(), thenComponent.describe());
        if (thenVisitor != null) {
            thenComponent.accept(thenVisitor);
        }

        if (elseComponent != null) {
            ComponentVisitor elseVisitor = visitor.visitComponent(elseComponent.getType(), elseComponent.getName(), elseComponent.describe());
            if (elseVisitor != null) {
                elseComponent.accept(elseVisitor);
            }
        }

        visitor.visitEnd();
    }

    @Override
    public void doValidate() {
        AssertUtil.notNull(condition, "IF[" + getName() + "] condition must not be null");
        AssertUtil.notNull(thenComponent, "IF[" + getName() + "] branch must not be null");

        thenComponent.validate();
        if (elseComponent != null) {
            elseComponent.validate();
        }
    }

    @Override
    public ComponentType getType() {
        return ComponentType.IF;
    }

    @Override
    public void reset() {
        super.reset();
        this.thenComponent.reset();
        if (elseComponent != null) {
            elseComponent.reset();
        }
    }
}
